using System;
using System.Threading.Tasks;
using AutoMapper;
using NBModular.Lib.Cache.Abstractions;
using NBModular.Module.WeChat.Application.WeChatUserService.ViewModels;
using NBModular.Module.WeChat.Domain.WeChat;
using NBModular.Module.WeChat.Domain.WeChatUser;
using NBModular.Module.WeChat.Domain.WeChatUser.Models;
using NBModular.Module.WeChat.Infrastructure;

namespace NBModular.Module.WeChat.Application.WeChatUserService
{
    public class WeChatUserService : IWeChatUserService
    {
        private readonly IMapper _mapper;
        private readonly IWeChatUserRepository _repository;
        private readonly IWeChatRepository _weChatRepository;
        private readonly ICacheHandler _cacheHandler;
        public WeChatUserService(
            IMapper mapper, 
            IWeChatUserRepository repository,
            IWeChatRepository weChatRepository,
            ICacheHandler cacheHandler)
        {
            _mapper = mapper;
            _repository = repository;
            _weChatRepository = weChatRepository;
            _cacheHandler = cacheHandler;
        }

        public async Task<IResultModel> Query(WeChatUserQueryModel model)
        {
            var result = new QueryResultModel<WeChatUserEntity>
            {
                Rows = await _repository.Query(model),
                Total = model.TotalCount
            };
            return ResultModel.Success(result);
        }

        public async Task<IResultModel> Add(WeChatUserAddModel model)
        {
            var entity = _mapper.Map<WeChatUserEntity>(model);
            //if (await _repository.Exists(entity))
            //{
                //return ResultModel.HasExists;
            //}

            var result = await _repository.AddAsync(entity);
            return ResultModel.Result(result);
        }

        public async Task<IResultModel> Delete(Guid id)
        {
            var result = await _repository.DeleteAsync(id);
            return ResultModel.Result(result);
        }

        public async Task<IResultModel> Edit(Guid id)
        {
            var entity = await _repository.GetAsync(id);
            if (entity == null)
                return ResultModel.NotExists;

            var model = _mapper.Map<WeChatUserUpdateModel>(entity);
            return ResultModel.Success(model);
        }

        public async Task<IResultModel> Update(WeChatUserUpdateModel model)
        {
            var entity = await _repository.GetAsync(model.Id);
            if (entity == null)
                return ResultModel.NotExists;

            _mapper.Map(model, entity);

            //if (await _repository.Exists(entity))
            //{
                //return ResultModel.HasExists;
            //}

            var result = await _repository.UpdateAsync(entity);

            return ResultModel.Result(result);
        }

        public async Task<IResultModel> GetUserInfoByAuthAsync(string appid, string code) 
        {
            var result = await _weChatRepository.GetUserInfoAsync(appid, code);
            if (result == null)
                return ResultModel.NotExists;

            var json = result.ToJObject();
            if (json.ContainsKey("errcode"))
                return ResultModel.Failed(result);

            WeChatUserModel model = new WeChatUserModel
            {
                AppId = appid,
                OpenId = json.ContainsKey("openid") ? (string)json["openid"] : "",
                NickName = json.ContainsKey("nickname") ? (string)json["nickname"] : "",
                Sex = json.ContainsKey("sex") ? (int)json["sex"] : -1,
                Province = json.ContainsKey("province") ? (string)json["province"] : "",
                City = json.ContainsKey("city") ? (string)json["city"] : "",
                Country = json.ContainsKey("country") ? (string)json["country"] : "",
                HeadimgUrl = json.ContainsKey("headimgurl") ? (string)json["headimgurl"] : "",
                Privilege = json.ContainsKey("privilege") ? (string)json["privilege"] : "",
                Unionid = json.ContainsKey("unionid") ? (string)json["unionid"] : ""
            };

            await _cacheHandler.UpdateAsync(CacheKeys.WECHAT_USER_INFO + model.OpenId, model,1440);

            var userEntity = await _repository.Find(x => x.OpenId == model.OpenId).FirstAsync();
            if (userEntity == null)
            {
                userEntity = _mapper.Map<WeChatUserEntity>(model);
                await _repository.AddAsync(userEntity);
                model.Id = await _weChatRepository.Find(x => x.AppId == appid).Select(s => s.Id).FirstAsync<Guid>();
            }
            else
            {
                model.Id = userEntity.Id;
                _mapper.Map(model, userEntity);
                await _repository.UpdateAsync(userEntity);
            }

            return ResultModel.Success(model);
        }

        public async Task<IResultModel> GetUserInfoByOpenidAsync(string openid) 
        {
            if (_cacheHandler.TryGetValue(CacheKeys.WECHAT_USER_INFO + openid, out WeChatUserModel model))
                return ResultModel.Success(model);

            var entity = await _repository.Find(x => x.OpenId == openid).FirstAsync();
            if (entity == null)
                return ResultModel.NotExists;

            model = _mapper.Map<WeChatUserModel>(entity);
            return ResultModel.Success(model);
        }

        public async Task<IResultModel> GetWxConfigAsync(string appid, string url) 
        {
            string key = appid;
            if (_cacheHandler.TryGetValue(CacheKeys.WECHAT_TICKET + key, out string ticket_data))
                return ResultModel.Success(await _weChatRepository.GetWxConfigAsync(appid, (string)ticket_data.ToJObject()["ticket"], url));

            _cacheHandler.TryGetValue(CacheKeys.WECHAT_AUTH_GLOBAL_ACCESS_TOKEN + key, out string token_data);
            if (token_data.IsNull())
                token_data = await _weChatRepository.GetTokenAsync(appid);
            if (token_data.IsNull())
                return ResultModel.NotExists;

            var json = token_data.ToJObject();
            if (json.ContainsKey("errcode"))
                return ResultModel.Failed(token_data);

            ticket_data = await _weChatRepository.GetTicketAsync(appid, (string)json["access_token"]);
            if (ticket_data.IsNull())
                return ResultModel.NotExists;

            json = ticket_data.ToJObject();
            if (json.ContainsKey("errcode") && (int)json["errcode"] != 0)
                return ResultModel.Failed(ticket_data);

            int minutes = 1;
            if (json.ContainsKey("create_time"))
            {
                var create_time = json["create_time"].ToDateTime();
                if (json.ContainsKey("expires_in"))
                {
                    var expires_in = json["expires_in"].ToInt() - 100;
                    if (create_time.AddSeconds(expires_in) > DateTime.Now)
                        minutes = (create_time.AddSeconds(expires_in) - DateTime.Now).TotalMinutes.ToInt();
                }
            }
            await _cacheHandler.SetAsync(CacheKeys.WECHAT_TICKET + key, ticket_data, minutes);

            return ResultModel.Success(await _weChatRepository.GetWxConfigAsync(appid, (string)json["ticket"], url));
        }
    }
}
